type putState =
  | AddL (* Add light spot *)
  | RemL (* Remove light spot *)
  | EdtL (* Edit light spot *)
  | Pick (* Selects a zone on the map *)
  | ChP (* Change user's pos *)

exception Not_Initied
let v = function
  | Some(e) -> e
  | None -> raise Not_Initied

class minimap (parent:GBin.frame) path labels leg reportsel_call =
  let box = GPack.vbox ~packing:parent#add ()
  in
    (*
  let bbox = GPack.button_box
               `HORIZONTAL
               ~layout:`SPREAD
               ~spacing:5
               ~packing:box#pack ()
  in
     *)
  let ev = GBin.event_box
             ~packing:box#add
             ()
  in
  let ar =
          GMisc.drawing_area
            ~width:200
            ~height:200
            ~packing:ev#add
            ()
  in
    (*
  let _ = Imgbutton.create bbox "addlight.xpm"
  in
  let _ = Imgbutton.create bbox "deletelight.xpm"
  in
     *)
  let img = GdkPixbuf.from_file path
  in
  let (initw,inith) = (GdkPixbuf.get_width img,GdkPixbuf.get_height img)
  in
  let drawable = lazy (new GDraw.drawable ar#misc#window)
  in

object(self)
  val mutable w = 200.
  val mutable h = 200.
  val mutable rapportx = 1.
  val mutable rapporty = 1.
  val mutable currentimg = img
  val area = ar
  val mutable position = (50.,50.,50.)
  val mutable spots = []
  val mutable putmode = Pick
  val mutable poschange = fun id _ _ -> ()
  val mutable buttondown = false
  (* selected region *)
  val mutable selected = false
  val mutable pt_sel = (0,0)
  val mutable scaleinv = 1.
  val mutable currbutton = 1
  val mutable evntactive = true
  method area =  box

  method active () =
    evntactive <- true

  method unactive () =
    evntactive <- false

  method to_global (x,y,z) =
    (x/.rapportx,y/.rapporty,z)
  method to_local (x,y,z) =
    (x*.rapportx,y*.rapporty,z)

  method report_sel_mod =
    reportsel_call selected;
    ()

  method change_scale sc =
    scaleinv<-1./.sc;
    let sw = w /.(float_of_int initw)
    in
    let  sh = h /.(float_of_int inith)
    in
      rapportx<-sw*.scaleinv;
      rapporty<-sh*.scaleinv


  method set_position p =
    let (x,y,z) = p in
    position <- (x,z,y);
    self#repaint ()

  method set_position_call p animate =
    self#set_position p;
    poschange 1 p animate
(*
  method get_editspot =
    let dlg = GWindow.dialog
                ~no_separator:true
                ~modal:true
                ()
    in
      dlg#vbox#add (Guismall.get_lightings_widgets_contener ())#coerce;
      dlg#show ()
 *)
  method touchspot (x,y) f l =
    let rec run = function
      [] -> ()
    | e::q ->
        let (sx,sy) = e in
          if(x < sx+.5. && x > sx-.5. && y < sy+.5. && y > sy-.5.) then
            f e
          else
            run q
    in
      run l

  method chposaction x y animate =
        let (_,_,z) = position in
        let (nx,ny,nz) = (self#to_global (x,y,z)) in
          self#set_position_call (nx,nz,ny) animate

  method buttonaction ev =
    let (x,y) = ((GdkEvent.Button.x ev),(GdkEvent.Button.y ev))
    in
      match GdkEvent.Button.button ev with
        | 1 ->
            currbutton <- 1;
            self#chposaction x y true
        | 3 ->
            currbutton <- 3;
            self#flood_fill (int_of_float x) (int_of_float y);
            self#repaint ()
        | _ -> ()
                (*
        match putmode with
            AddL ->
              spots <- (x,y)::spots
          | RemL ->
              () (*self#touchspot (x,y) (fun e -> List.remove e)*)
          | EdtL ->()
          | ChP ->
              self#chposaction x y
          | Pick ->
              self#flood_fill (int_of_float x) (int_of_float y);
              self#repaint ()
                 *)

  method buttonmove ev =
    let (x,y) = ((GdkEvent.Motion.x ev),(GdkEvent.Motion.y ev))
    in
      if currbutton = 1 then
        self#chposaction x y false

  method reshape a =
    currentimg <- GdkPixbuf.create
                   ~width:a.Gtk.width
                   ~height:a.Gtk.height
                   ()
                    ;
        (*currback <- new GDraw.drawable ~width:a.Gtk.width
                  ~height:a.Gtk.height
                  ~window:area#misc#window
                  ()
                  ;
     *)
    let fw = float_of_int (a.Gtk.width)
    in
    let fh = float_of_int (a.Gtk.height)
    in
    let sw = fw /.(float_of_int initw)
    in
    let  sh = fh /.(float_of_int inith)
    in
      GdkPixbuf.scale
        ~dest:currentimg
        ~width:a.Gtk.width
        ~height:a.Gtk.height
        ~scale_x:sw
        ~scale_y:sh
        img;
      w<-fw;
      h<-fh;
      rapportx<-sw*.scaleinv;
      rapporty<-sh*.scaleinv;
      if selected then
        begin
          self#reinit_labels_only;
          self#reinit_flood;
        end;
      self#repaint ()


  method paint_spotlights (drawable:GDraw.drawable) l =
    let rec do_paint = function
        [] -> ()
      | (x,y)::q ->
          (
          drawable#rectangle
            ~x:(int_of_float(x -. 3.))
            ~y:(int_of_float(y -. 3.))
            ~width:6
            ~height:6
            ();
          do_paint q
          )
    in
      do_paint l

  method set_pixel gptr rstride chanel (r,g,b) px py =
    let offset = (px*chanel +py*rstride) in
      Gpointer.set_byte gptr ~pos:offset r;
      Gpointer.set_byte gptr ~pos:(offset + 1) g;
      Gpointer.set_byte gptr ~pos:(offset + 2) b

  method get_pixel gptr rstride chanel px py =
    let offset = (px*chanel +py*rstride) in
    (
      Gpointer.get_byte gptr ~pos:offset,
      Gpointer.get_byte gptr ~pos:(offset + 1),
      Gpointer.get_byte gptr ~pos:(offset + 2)
    )

  method inv_pix (r,g,b) =
    (255 - r,255 - g, 255 - b)

  method flood
      (markf:int * int -> unit)
      markg wM hM
      (f:int -> int -> unit) x y inc =
    let rec floodr x y =
      markf (x,y);
      f x y;
      if ((x+inc) < wM) && not(markg ((x+inc),y)) then
        floodr (x+inc) (y);
      if ((x - inc) >= 0) && not(markg ((x-inc),y)) then
        floodr (x - inc) (y);
      if (y-inc >= 0) && not(markg((x),y-inc)) then
        floodr (x) (y-inc);
      if (y+inc < hM) && not(markg((x),y+inc)) then
      floodr (x) (y+inc)
    in
      floodr x y

  method reinit_labels_only =
    for i = 0 to initw - 1 do
      for j = 0 to inith -1 do
        labels.(i).(j) <- (abs labels.(i).(j))
      done
    done

  method reinit_labels gptr rstride chanel =
    let (fw,fh) = (w /. (float_of_int initw),h /. (float_of_int inith))
    in
    for i = 0 to initw - 1 do
      for j = 0 to inith -1 do
        let lbl = labels.(i).(j)
        in
        if lbl < 0 then
          begin
            labels.(i).(j) <- (- lbl);
            let (cr,cg,cb) = (Hashtbl.find leg (-lbl))
            in
            self#set_pixel gptr rstride chanel
             (int_of_float (cr*.255.),
              int_of_float (cg*.255.),
              int_of_float (cb*.255.)
             )
              (int_of_float
                 ((float_of_int i)*.fw))
              (int_of_float ((float_of_int j)*.fh));
          end
      done
    done

  method reinit_flood =
    let (fw,fh) = (w /. (float_of_int initw),h /. (float_of_int inith))
    in
    let (x,y) = pt_sel
    in
    let initlbl =
      labels.(
        int_of_float((float_of_int x) /. fw)).(
          int_of_float((float_of_int y) /. fh))
    in
    let gptr = GdkPixbuf.get_pixels currentimg
    in
    let (rstride,chanel) =
      (GdkPixbuf.get_rowstride currentimg,
       GdkPixbuf.get_n_channels currentimg)
    in
    let invcolor = self#inv_pix (self#get_pixel gptr rstride chanel x y)
    in
      self#flood
        (fun (i,j) -> labels.(i).(j) <- -labels.(i).(j))
        (fun (i,j) -> labels.(i).(j) < 0 || labels.(i).(j) <> initlbl)
        initw inith
        (fun i j ->
           let ri = int_of_float ((float_of_int i) *. fw)
           and rj = int_of_float ((float_of_int j) *. fh)
           in
             if (ri mod 2 = rj mod 2) then
               self#set_pixel gptr rstride chanel invcolor ri rj
        )
        (int_of_float((float_of_int x) /. fw))
        (int_of_float((float_of_int y) /. fh)) 1

  method flood_fill x y =
    let (fw,fh) = (w /. (float_of_int initw),h /. (float_of_int inith))
    in
    let gptr = GdkPixbuf.get_pixels currentimg
    in
    let (rstride,chanel) =
      (GdkPixbuf.get_rowstride currentimg,
       GdkPixbuf.get_n_channels currentimg)
    in
    let initlbl =
      labels.(
        int_of_float((float_of_int x) /. fw)).(
          int_of_float((float_of_int y) /. fh))
    in
      if selected then
        begin
          selected <- false;
          self#reinit_labels gptr rstride chanel;
        end;
      if initlbl > 0 then
        begin
          selected <- true;
          pt_sel <- (x,y);
          self#reinit_flood
        end;
      self#report_sel_mod




  method repaint () =
    let drawable = Lazy.force drawable in
      drawable#put_pixbuf
        ~x:0
        ~y:0
        currentimg;
      self#paint_spotlights drawable spots;
      let (x,y,_) = (self#to_local position) in
        if (x >= 0. && y >= 0. && y<h && x<w) then
          begin
            drawable#set_foreground (`NAME("yellow"));
          drawable#rectangle
            ~x:(int_of_float(x -. 3.))
            ~y:(int_of_float(y -. 3.))
                                  ~width:6
                                  ~height:6
                                  ()
          end
         else
          begin
            drawable#set_foreground (`NAME("red"));
            let (px,py) =
              ((if x<0. then 0. else if x>=w then (w-.1.) else x),
               (if y<0. then 0. else if y>=h then (h-.1.) else y)
              )
            in
            let dir = {Geometry.x=(x -. px)/.5.;
                       Geometry.y=(y -. py)/.5.;
                       Geometry.z = 0.}
            in
              drawable#line
                 ~x:(int_of_float (px -. dir.Geometry.x ))
                 ~y:(int_of_float (py -. dir.Geometry.y ))
                 ~x:(int_of_float px)
                 ~y:(int_of_float py)
          end


  method init poschangecall currscale () =
    (*currback <- Some (new GDraw.drawable ~width:a.Gtk.width
                  ~height:a.Gtk.height
                  ~window:area#misc#window
                  ())
                  ;
     *)
    scaleinv <- 1./.currscale;
    poschange <- poschangecall;
    ev#event#add [`BUTTON_PRESS];
    ev#event#add [`BUTTON_RELEASE];
    ev#event#add [`POINTER_MOTION];
    ignore(ev#event#connect#button_release ~callback:(fun ev ->
                                                if evntactive then
                                                  buttondown <- false;
                                                evntactive));
    ignore(ev#event#connect#button_press ~callback:(fun ev ->
                                              if evntactive then
                                                begin
                                                buttondown <- true;
                                                self#buttonaction ev;
                                                end;
                                              evntactive));
    ignore(ev#event#connect#motion_notify
             ~callback:(fun ev -> if evntactive
                        && buttondown then
                          self#buttonmove ev; evntactive));
    currentimg <- img;
    ar#misc#realize ();
    self#repaint ();
    ignore(ev#misc#connect#size_allocate ~callback:self#reshape);
    ar#event#connect#expose
      ~callback:(fun _ -> self#repaint (); false);
end
